package calcul;

import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MaximizeAction;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Random.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MonteCarlo {
	
	private int callPutFlag;
	private double stockPrice;
	private double strikePrice;
	private double maturityTime;
	private double discountRate;
	private double carryRate;
	private double volatility;
	private int nSteps;
	private int nSimulations;
	
	public static int CALL = 0;
	public static int PUT = 1;
	
	public MonteCarlo(int cPF, double stock, double strike, double m, double dR, double cR, double v, int nSte, int nSim)
	{
		callPutFlag = cPF;
		stockPrice = stock;	
		strikePrice = strike;
		maturityTime = m;
		discountRate = dR / 100.0;
		carryRate = cR / 100.0;
		volatility = v / 100.0;
		nSteps = nSte;
		nSimulations = nSim;
	}
	
	public double calcul()
	{
		double dt, St;
		double Sum = 0, Drift, vSqrdt;
		int i, j, z;
		double temp;
		Random r = new Random();
		
		System.out.println("Begin Calcul");
		
		dt = maturityTime / (double)nSteps;
		Drift = (carryRate - ((volatility * volatility) / 2)) * dt;
		vSqrdt = volatility * java.lang.Math.sqrt(dt);
		
		if (callPutFlag == CALL)
			z = 1;
		else
			z = -1;
		
		for (i=0 ; i<nSimulations ; i++)
		{
			St = stockPrice;
			
			for (j=0 ; j<nSteps ; j++)
			{
				St = St * java.lang.Math.exp(Drift * vSqrdt * r.nextGaussian());
			}
			
			temp = z * (St - strikePrice);
			if (temp > 0)
				Sum = Sum + temp;
		}
		
		System.out.println("Sum : " + Sum);
		System.out.println("result : " + java.lang.Math.exp( ((-discountRate) * maturityTime) * (Sum / nSimulations)));
		
		return java.lang.Math.exp( ((-discountRate) * maturityTime) * (Sum / nSimulations));
	}
	
	public double calculThread(int threadNB)
	{
		double dt, St;
		double Sum = 0, Drift, vSqrdt;
		int z;
		double temp;
		Random r = new Random();
		ArrayList<MyThread> th = new ArrayList<MyThread>();
		List<Future<Double>> result = null;
		
		dt = maturityTime / (double)nSteps;
		Drift = (carryRate - ((volatility * volatility) / 2)) * dt;
		vSqrdt = volatility * java.lang.Math.sqrt(dt);
		
		if (callPutFlag == CALL)
			z = 1;
		else
			z = -1;
		
		System.out.println("Number of Thread : " + threadNB);
		
		// Thread Initialization		
	    for(int i=0 ; i<threadNB ; i++)
			th.add(new MyThread(stockPrice, strikePrice, nSteps, nSimulations / threadNB, Drift, vSqrdt, z, r));
	        
	    final ExecutorService executorPool = Executors.newFixedThreadPool(threadNB);    
	    
	    try {
	    	
			result = executorPool.invokeAll(th, 100000, TimeUnit.SECONDS);
			
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	    
		for (int i=0 ; i<threadNB ; i++)
		{
			try {
				
				Sum += result.get(i).get();
				System.out.println(i + " : " + result.get(i).get());
				
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		System.out.println("Sum : " + Sum);
		System.out.println("result : " + java.lang.Math.exp( ((-discountRate) * maturityTime) * (Sum / nSimulations)));
		
		executorPool.shutdown();
		
		return java.lang.Math.exp( ((-discountRate) * maturityTime) * (Sum / nSimulations));
	}
	
	public static double calculPartial(double stockPrice, double strikePrice, int nSteps, int nSimulations, double Drift, double vSqrdt, int z, Random r)
	{
		double Sum = 0;
		double temp;
		double St;
		Lock l = new ReentrantLock();
		
		System.out.println("NbSim : " + nSimulations);

		l.lock();
		
		for (int i=0 ; i<nSimulations ; i++)
		{
			St = stockPrice;
			
			for (int j=0 ; j<nSteps ; j++)
			{
				St = St * java.lang.Math.exp(Drift * vSqrdt * r.nextGaussian());
			}
			
			temp = z * (St - strikePrice);
			if (temp > 0)
				Sum = Sum + temp;
		}
		
		l.unlock();
		
		return Sum;
	}

}
